/*******************************************************************************

  Pilot Intelligence Library
    http://www.pilotintelligence.com/

  ----------------------------------------------------------------------------

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program. If not, see <http://www.gnu.org/licenses/>.

*******************************************************************************/

#ifndef __SOCKET_PP_POCO_H__
#define __SOCKET_PP_POCO_H__


#include <stdint.h>
#include <string>

#include "base/types/SPtr.h"


namespace pi {


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

///
/// \brief The Socket type enum
///
enum RSocketType
{
    SOCKET_TCP,                         ///< TCP
    SOCKET_UDP,                         ///< UDP
    SOCKET_UDP_MULTICAST,               ///< multicast UDP
};


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

///
/// \brief The socket address class
///
class RSocketAddress
{
public:
    RSocketAddress() {
        address = "";
        port = -1;

        type = SOCKET_TCP;
    }

    ~RSocketAddress() {
        address = "";
        port = -1;

        type = SOCKET_TCP;
    }

public:
    std::string     address;                ///> host address
    int             port;                   ///> port number

    RSocketType     type;                   ///> socket type
};


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

class RSocketImpl;          // socket implementation

///
/// \brief The RSocket class support TCP/UDP/UDP multicast, where the usage is simple.
///
/// For server (receive message):
///     startServer
///     recv
/// For client (deliver message):
///     startClient
///     send
///
class RSocket
{
public:
    /// The mode argument to poll() and select().
    enum SelectMode {
        SELECT_READ  = 1,                   ///> for reading
        SELECT_WRITE = 2,                   ///> for writting
        SELECT_ERROR = 4                    ///> for error
    };

public:
    RSocket();
    virtual ~RSocket();

    /////////////////////////////////////////////////////////////////
    /// start server or client
    /////////////////////////////////////////////////////////////////

    ///
    /// \brief startServer
    ///
    /// \param port             - port number
    /// \param t                - socket type
    ///
    /// \return
    ///        0                - success
    ///        other            - failed
    ///
    int startServer(int port, RSocketType t=SOCKET_TCP);

    ///
    /// \brief startServer
    ///
    /// \param addr             - host or group address
    /// \param port             - port number
    /// \param t                - socket type
    ///
    /// \return
    ///        0                - success
    ///        other            - failed
    ///
    int startServer(const std::string& addr, int port,
                    RSocketType t=SOCKET_UDP_MULTICAST);


    ///
    /// \brief startClient
    ///
    /// \param host             - pair host address or group address
    /// \param port             - port number
    /// \param t                - socket type
    ///
    /// \return
    ///        0                - success
    ///        other            - failed
    ///
    int startClient(const std::string& host, int port,
                    RSocketType t=SOCKET_TCP);


    ///
    /// \brief close the socket
    ///
    /// \return
    ///
    int close(void);


    /////////////////////////////////////////////////////////////////
    /// Data Transmission
    /////////////////////////////////////////////////////////////////

    ///
    /// \brief send data to remote host
    ///
    /// \param dat              - data buffer
    /// \param len              - data length
    ///
    /// \return
    ///        > 0              - actual sended bytes
    ///        <= 0             - failed
    ///
    int send(const void* dat, int len);

    ///
    /// \brief send string to remote host
    ///
    /// \param msg              - std::string
    ///
    /// \return
    ///        > 0              - actual sended bytes
    ///        <= 0             - failed
    ///
    int send(const std::string& msg);


    ///
    /// \brief receive data from remote host
    ///
    /// \param dat              - data buffer
    /// \param len              - data length
    ///
    /// \return
    ///        > 0              - actual received bytes
    ///        <= 0             - failed
    ///
    int recv(void* dat, int len, RSocketAddress* sender=NULL);

    ///
    /// \brief receive string from remote host
    ///
    /// \param msg              - std::string
    /// \param maxLen           - maximum string length
    ///
    /// \return
    ///        > 0              - actual received bytes
    ///        <= 0             - failed
    ///
    int recv(std::string& msg, int maxLen = 4096, RSocketAddress* sender=NULL);


    ///
    /// \brief receive data until given data length reached
    ///
    /// \param dat              - data buffer
    /// \param len              - data length
    ///
    /// \return
    ///        > 0              - actual received bytes
    ///        <= 0             - failed
    ///
    int recvUntil(void* dat, int len, RSocketAddress* sender=NULL);


    ///
    /// \brief Determines the status of the socket, using a
    ///             call to select().
    ///        The mode argument is constructed by combining the values
    ///             of the SelectMode enumeration.
    ///
    /// \param
    ///     timeout             - millsecond for timeout
    ///     mode                - SELECT_READ       - for reading
    ///                           SELECT_WRITE      - for writing
    ///                           SELECT_ERROR      - for error
    /// \return
    ///     Returns true if the next operation corresponding to
    ///         mode will not block, false otherwise.
    int pool(int64_t timeout, int mode);

    ///
    ///  \brief Returns the number of bytes available that can be read
    ///             without causing the socket to block.
    ///
    int available(void);


    /////////////////////////////////////////////////////////////////
    /// socket address
    /////////////////////////////////////////////////////////////////

    // get address
    int getMyAddress(RSocketAddress &a);
    int getClientAddress(RSocketAddress &a);


    /////////////////////////////////////////////////////////////////
    /// socket options & status
    /////////////////////////////////////////////////////////////////

    // socket options
    int setNonBlocking(int nb=1);
    int getNonBlocking(void);

    int setReuseAddr(int reuse=1);
    int getReuseAddr(void);

    // status
    int isOpened(void);
    bool isServer(void);


    /////////////////////////////////////////////////////////////////
    /// socket low-level functions
    /////////////////////////////////////////////////////////////////

    // server functions
    int bind(int port);
    int listen(void);
    int accept(RSocket& s);

    // client functions
    int connect(const std::string& host, int port);


protected:
    friend class RSocketImpl;
    SPtr<RSocketImpl>   m_impl;                         ///< implementation
};


} // end of namespace pi

#endif // end of __SOCKET_PP_POCO_H__

